<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0" xml:lang="ja">
<channel>
<title>リフォームガイド</title>
<link>http://plaza-r.co.jp/guide/</link>
<atom:link href="http://plaza-r.co.jp/guide/rss2.xml" rel="self" type="application/rss+xml" />
<language>ja</language>
<copyright>Copyright (C) 2026 リフォームガイド All rights reserved.</copyright>
<lastBuildDate>Sat, 29 Aug 2026 16:00:17 +0900</lastBuildDate>
<generator>a-blog cms</generator>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>テストエントリ</title>
<link>http://plaza-r.co.jp/guide/entry-12764.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading02">これはテストエントリーです</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit01">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">



































<!-- media -->
<div class="column-media-center">

<a href="http://plaza-r.co.jp/media/027/202608/shop_card_thumb02.png?v=20260819155107"data-rel="SmartPhoto">
<img class="columnImage"
 src="http://plaza-r.co.jp/media/027/202608/mode3_w282-shop_card_thumb02.png?v=20260819155107"
 alt="八幡店の外観">
</a>


</div>
















<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。 またそのなかでいっしょになったたくさんのひとたち、ファゼーロとロザーロ、羊飼のミーロや、顔の赤いこどもたち、地主のテーモ、山猫博士のボーガント・デストゥパーゴなど、いまこの暗い巨きな石の建物のなかで考えていると、みんなむかし風のなつかしい青い幻燈のように思われます。では、わたくしはいつかの小さなみだしをつけながら、しずかにあの年のイーハトーヴォの五月から十月までを書きつけましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->

<p class="text01">あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。 またそのなかでいっしょになったたくさんのひとたち、ファゼーロとロザーロ、羊飼のミーロや、顔の赤いこどもたち、地主のテーモ、山猫博士のボーガント・デストゥパーゴなど、いまこの暗い巨きな石の建物のなかで考えていると、みんなむかし風のなつかしい青い幻燈のように思われます。では、わたくしはいつかの小さなみだしをつけながら、しずかにあの年のイーハトーヴォの五月から十月までを書きつけましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/entry-12764.html</guid>
<pubDate>Sat, 22 Aug 2026 12:54:44 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>屋根塗装の流れ</title>
<link>http://plaza-r.co.jp/guide/reform-situation/constflow-roofpaint.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">屋根塗装の工事の一般的な流れをご説明します。</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">01 高圧洗浄</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">洗浄器を利用し、こびりついた苔や藻、汚れ、カビなどを高圧の水で洗い流していきます。<br />
高圧洗浄器が使用できない場合は手作業で落としていきます。<br />
※屋根の勾配が大きい場合は、屋根足場を事前に設置する場合もあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/0b1a3d3feff5606145fddbc2108e6ee4.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">02 鉄部下地調整</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">棟板金やケラバ板金などの鉄部のサビを落としたり、密着性を高め塗装が剥がれにくくするために、研磨を行います。<br />
ついつい飛ばされがちな工程ですが、この作業が耐久性に大きく影響を及ぼします。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/febf5751e19d07bd7af05c68e9a17354.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">03 鉄部さび止め塗装</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">屋根全体を塗る前に、鉄部にのみさび止め塗装をしておきます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/184ed23a1332ab28638b64ff5484d0df.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">04 屋根下塗り塗装</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">後から塗装がはがれないように、シーラーという塗料を下塗りします。これは接着剤の効果を持ち、密着度を高める効果があります。<br />
この工程で手抜きをすると、塗料が長持ちせず、劣化が早くなります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/2fe4dabc7095e1ef6b45f342788c2fa7.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">05 屋根中塗り</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">シーラーが乾いたら、塗膜に厚みを持たせるため、中塗を行います。鉄部も屋根と同ランク以上の高品質塗料で塗装をすることが原則です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/0bcdd55e1a76b61d87d3d4792175a2dc.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">06 屋根上塗り</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">中塗りをした上から、仕上げの上塗りをします。<br />
上塗りをすることで、つやが出てきます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/b9a1f91e6caeb00838f64001b70bf7cc.jpg"
 alt=""
 width="250"
 height="251">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">07 確認作業</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">細かい塗料の拭き残しや塗り残し、塗りムラの確認作業を行います。<br />
最終チェックを行い、塗りなおしを防ぎます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/48ae8dfc4759a9dc1d49520fd8925fc0.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">08 清掃</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">塗り残しや、塗りムラのチェックを行った後に建物周辺や敷地内外の掃除を行い、工事の完了となります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/24a5052badbdd196e24cd532b31a43b2.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>目的・状況別リフォーム</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/reform-situation/constflow-roofpaint.html</guid>
<pubDate>Wed, 24 Aug 2016 15:04:56 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>外壁塗装の流れ</title>
<link>http://plaza-r.co.jp/guide/reform-situation/constflow-wallpaint.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">外壁塗装の工事の一般的な流れ</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">01 足場組立</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">塗り替え工事を行うとき、まず、はじめに安全性や施工品質を維持するため、足場組立をします。基本となる足場の組み立ては、慎重に行います。足元を安定させる足場は、よい塗装工事の基本となります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/9e41e3c29ca46c6cb9d9e255b5d7b22c.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">02 ネット養生</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">周辺のお宅や植木、車などが汚れないよう専用のネットで足場を覆います。<br />
塗料などが飛び散り、汚れるのを防ぎます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/a242e95c7e25214ee8786c2ae8b578e0.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">03 高圧洗浄</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">洗浄機を使用し、外壁のホコリ、汚れ、藻、コケ、カビ等を高圧の水で洗い流していきます。<br />
高圧洗浄機が使用できない場合は手作業で落としていきます。<br />
これらの作業を丹念に行うことで、塗料の性能が十分に発揮されます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/5d9ddf26fe4bd3d0ace3b741a26d3bab.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">04 下地処理</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">サイディングやモルタルのひび割れなどを塗装前にしっかりと補修します。<br />
また、コーキングが劣化している場合は、コーキングを打ち替えたり、増し打ちをします。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/a303f2b6d43a9adee8faea18e983e8be.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">05 養生</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">塗装を行わない場所(アルミサッシなど)を養生シートなどでしっかり覆います。この養生をしっかり行わないと、仕上がりが不十分なものとなるため、丁寧に行います。<br />
塗り替え場所以外への汚れは絶対に残さないのがプロの仕事です</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/3dc33ea97166ee101675d9af70cb0a82.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">06 下塗り</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">シーラー（プライマー）などの材料で下塗りを行います。<br />
下塗りを行うことで、中塗り塗料との密着度を高めます。<br />
この工程で手抜きをすると、塗料が長持ちせず、劣化が早くなります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/27e3663499be507e37aa8bcc3571c68e.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">07 中塗り</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">塗膜に厚みをもたせるために中塗りを行います。汚れや、カビが気になる外壁には、高品質塗料で再発を防ぐなど、家の傷みの状況に応じた塗装を施していきます。<br />
木部と鉄部は外壁と同ランク以上の高品質塗料で塗装することが原則です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/ee295c643438ca643154af577c284cce.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->


<h2 class="heading02"><span class="color-blue f20-16">08 上塗り</span></h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">外壁は３回塗りの３層仕上げが一般的ですが、上塗りが３層目の仕上げです。<br />
陽のあたる外壁面と陰になる外壁面で、傷み方も変わってきますので、塗る回数も変わることがありますが、基本は３回塗りで仕上げます。<br />
上塗りは、見栄えに直接関わる大切な工程です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/effead4606c1d1b8889843d5b5f15f51.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">09 完工チェック</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">細かい塗料の拭き残しや塗り残し、塗りムラの確認作業を行います。<br />
足場を解体してからではやり直しは不可能となってしまうため、足場解体前にしっかりとしたチェックを行います。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/d8fc152d5e080edfd56485a2d42954fc.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->



<h3 class="heading03"><span class="color-blue f20-16">10 足場解体・清掃</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">









<!-- テキスト -->

<p class="text01">塗り残しや、塗りムラのチェックを最後に行った後は、養生シートを取り外し、足場の解体を行います。<br />
そして最後に建物周辺や敷地内外の掃除を行い、工事の完了となります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/708bcd409c628442a22e3fa4fa805868.jpg"
 alt=""
 width="250"
 height="250">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>目的・状況別リフォーム</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/reform-situation/constflow-wallpaint.html</guid>
<pubDate>Wed, 24 Aug 2016 14:40:19 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>塗料の種類</title>
<link>http://plaza-r.co.jp/guide/choose-equipment/paint-type.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">お客様</span>に<span class="color-blue">最適</span>な<span class="color-blue">塗料</span>をご提案いたします。</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">塗料を用途に合わせて正しく選択しないと、本来の耐久性を発揮できず、想定よりも短い期間で再塗装が必要になる恐れがあります。建物の長期保護とライフサイクルコストの最適化を目指した高性能な最新塗料も多数ラインアップされていますので、用途・性能に合わせて最適な塗料をお選び下さい。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">外壁・屋根塗装に使われる塗料</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">主に以下の種類があります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->



























<!-- テーブル -->
<div class="column-table">
  <div >
  <table class="table01 is_scrollHint">
<tr>
<th class="acms-cell-text-nowrap acms-admin-cell-text-nowrap">塗料の種類</th>
<th class="acms-cell-text-nowrap acms-admin-cell-text-nowrap">想定耐久年数目安</th>
<th>特徴</th>
</tr>
<tr>
<td>アクリル系塗料</td>
<td>3〜5年</td>
<td>硬化が早く、取り扱いやすい塗料で、他の塗料に比べてコストは安価です。耐用年数が短く、外壁や建物外部の塗装にはやや不向きです。</td>
</tr>
<tr>
<td>ウレタン系塗料</td>
<td>5〜8年</td>
<td>防汚性、耐久性がそれぞれ標準レベルのポリウレタン樹脂を配合した塗料です。安価ではありますが、シリコン樹脂塗料等と比較するとやや性能は劣ります。</td>
</tr>
<tr>
<td>シリコン系塗料</td>
<td>8〜12年</td>
<td>高耐候性と低汚染性をあわせもつシリコン樹脂を配合した塗料です。外壁や屋根塗装でよく使われているポピュラーな材料です。</td>
</tr>
<tr>
<td>ラジカル制御型塗料</td>
<td>12〜15年</td>
<td>塗膜劣化原因の一つである「ラジカル」の発生を抑制する技術を導入した新しい塗料です。高い耐候性を発揮し、厳しい自然環境から建物の長期保護を可能にします。</td>
</tr>
<tr>
<td>フッ素系塗料</td>
<td>12〜20年</td>
<td>超耐久性に定評のあるフッ素樹脂を配合した塗料です。汚れにくく耐久性の高い塗膜を提供し、建物の資産価値を高めます。</td>
</tr>
</table>

  </div>
</div>
































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>設備・商品の選び方</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/choose-equipment/paint-type.html</guid>
<pubDate>Wed, 24 Aug 2016 13:56:56 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>塗り替えの基礎知識</title>
<link>http://plaza-r.co.jp/guide/knowledge/paint-basic.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">失敗しない</span>ための<span class="color-blue">塗り替えリフォーム</span>の<span class="color-blue">基礎知識</span>を<span class="color-blue">ご紹介</span>いたします。</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">「そろそろ我が家も外壁や屋根のメンテナンス時期かな……」と思っても、具体的にどうすればいいのか分からず、劣化を放置してしまうケースは少なくありません。一般の方にとって、塗装の専門知識がないのは当然のことです。そこで、失敗しない塗り替えリフォームを実現するために、ぜひ知っておいていただきたい基礎知識をまとめました。大切な住まいを守るための参考にしてください。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">１</span> 塗装の目的は</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<a href="http://plaza-r.co.jp/archives/014/201608/large-75e76decce83c0917aaf92f3b2af2233.png"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/75e76decce83c0917aaf92f3b2af2233.png"
 alt=""
 width="486"
 height="770">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">２</span> 外壁劣化の始まり</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit03">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/3e6afae4a5577937cd3dce2138606738.jpg"
 alt=""
 width="250"
 height="177">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">こすると手が白くなるチョーキング</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">紫外線や水の影響で塗料に含まれている樹脂が劣化して粉状に。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/8d964d7a9dc484cd4b8279c350457104.jpg"
 alt=""
 width="250"
 height="178">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">ひびわれ</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">放っておくと雨水が染み込んだり、外壁が剥がれたりします。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/ccf8814fe52663a2769110d0deaf47d1.jpg"
 alt=""
 width="250"
 height="178">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">カビや藻</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">防水機能が低下しているサイン。見た目も悪くなります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/6b7d11e354840c6705eac4b7aec2251d.jpg"
 alt=""
 width="250"
 height="177">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">室内の天井にシミ</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">外壁や屋根からの雨漏りの可能性があります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/202107/eb9a7a7e49f45587be72a58c063d98be.png"
 alt=""
 width="700"
 height="140">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">３</span> 塗料とは</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/afe9b94c1bb1903dd8373696d964e64d.jpg"
 alt=""
 width="700"
 height="253">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">→<a href="/paint/paint-type/">塗料の種類はこちら</a></p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">４</span> 塗装とは</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">塗装とは、それぞれの役割を持った塗料を塗り重ねて塗膜という膜をつくる工事です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/f381bb4984a171d92cb584d280c207c2.jpg"
 alt=""
 width="700"
 height="186">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">５ 塗り替えの品質とは</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">「塗料」は「塗装」という施工をしてはじめて「塗膜」という製品になります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/6c11e873bab4bb05c79763b5193158ff.jpg"
 alt=""
 width="700"
 height="213">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">ワンポイントアドバイス</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04"><span class="color-blue">失敗しない塗装工事のポイント</span></h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">塗り替えリフォーム成功のカギは、高い技術を持ち、しかも素材や塗料について十分な知識を備えている会社にお願いすることです。そのためには信頼できる会社に相談し、納得するまで質問をしてみましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">ハウジングプラザのこだわり</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">ハウジングプラザは、塗装の技術を磨くだけでなく、常に塗料について勉強しています。それが塗り替え品質を上げるために必要だとよく知っているからです。<br />
各メーカー主催の塗料についての研修会などにも参加して、どうすれば最高の品質を引き出せるか、日々研究を重ねています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">６</span> 最良施工の条件</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">作業する職人の技術力が高くても、メーカーの指定する塗装仕様を守って正しいプロセスで施工しなければ塗料本来の性能を発揮できません。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03"><span class="color-blue">最良施工3つの条件</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/42cbd4c8b58b5d896a932ba99f0abc85.png"
 alt=""
 width="700"
 height="202">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">７</span> 塗り替え費用の構成</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/7546195ee6a4e47555a8a9ae6ec77cca.jpg"
 alt=""
 width="700"
 height="156">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/d1ff165b2f39d0e45d93af3abd3e8bce.jpg"
 alt=""
 width="700"
 height="156">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">ワンポイントアドバイス</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04"><span class="color-blue">納得できる業者選びのコツ</span></h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">塗り替えリフォームを思い立ったら、まずは複数の会社から見積を取ってみましょう。その見積で、誠実な業者かどうかある程度判断できます。塗り替えリフォームの場合「早くて安い」は良いことではありませんので、価格だけで決めてしまうのは危険です。その会社の「塗り替えについての思い」や「こだわり」を確認し、納得した業者を選びましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">ハウジングプラザのこだわり</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">ちょっと高くても品質の良い塗料を使いたい、それが私たちの本音です。心をこめて仕上げる塗り替えリフォームは、１日でも長持ちしてもらいたいと心から願っているのです。<br />
でも、お金を出すのはお客様ですから、きちんと納得して選んでいただけるよう、わかりやすい説明を心がけています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02"><span class="color-blue">８</span> 標準的な塗装工程</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/014/201608/d422dc6b7cacb1c61c9847745161e999.jpg"
 alt=""
 width="700"
 height="337">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>リフォームの基礎知識</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/knowledge/paint-basic.html</guid>
<pubDate>Wed, 24 Aug 2016 10:01:44 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>二世帯リフォーム</title>
<link>http://plaza-r.co.jp/guide/reform-situation/pickup-twogenehouse.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">「親子で快適に、安全に、安心して暮らしたい」</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04"><span class="bg-circle3 f16">部分共有型</span></h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">完全同居型二世帯リフォームのメリット・デメリット</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">二世帯住宅とは？</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">二世帯住宅の種類</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">完全同居型</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">完全分離型</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">家族の寝室などのプライバシー面は分け、その他のスペース（リビング、キッチン、トイレ、浴室など）や設備は共有するのが、「完全同居型二世帯住宅」。完全同居型二世帯住宅へのリフォームは、親世帯と子世帯がひとつの大家族として暮らす家にリフォームすることになります。完全同居型二世帯リフォームにおける間取りは、</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>１階に二世帯の共用部分である玄関、リビング、キッチン、浴室、トイレや親世帯の寝室を配置</li>
<li>２階に子世帯の寝室や子供部屋を配置</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">というのが一般的です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 
<h5 class="heading05">メリット</h5>

























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">水まわりを共有するため、完全独立型と比べて工事費や設備費などの建築コストを大幅に抑えることができます。 また、基本料金のかかる水道光熱費や通信費を世帯で一本化できるため、節約効果も高くなります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">2.土地を有効活用できる<br />
さまざまなスペース・設備を共有するため、狭小地や限られた広さあっても比較的ゆったりと住まうことが可能です。<br />
3.大家族ならではの楽しさを満喫できる<br />
各世代間のコミュニケーションが豊かに取れ、家風といった目に見えない価値を継承することができます。<br />
デメリット<br />
1.共用部分が使いづらくなる<br />
親世帯・子世帯のどちらかに来客があるとき、もう一方の世帯がリビングに行くのを遠慮してしまうなど、共用部分が使いづらい場面が生じることがあります。<br />
2.生活時間のずれがストレスになる<br />
親世帯と子世帯では生活時間が異なることが多いため、夜間・深夜の共用部分での音などがストレスになる場合があります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">１つの建物を上下または左右に分け、それぞれ個別に住宅としての機能を持たせたもの。１つの建物ではなく、２つの建物を庭や通路でつないだものもあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">二世帯の住まい方の希望や状況に応じて、いくつかのタイプに分類できます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 
<h5 class="heading05">１．玄関のみ共用</h5>

























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">玄関を除くすべての部位（LDKや水まわり）は世帯ごとに別々にするタイプです。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 
<h5 class="heading05">２．玄関・浴室のみ共用</h5>

























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">キッチン・リビング・トイレは別々に設けます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 
<h5 class="heading05">３．玄関・浴室・リビングのみ共用</h5>

























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">キッチン・トイレは別々に設けます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">家族の寝室などのプライバシー面は分け、その他のスペース（リビング、キッチン、トイレ、浴室など）や設備は共用します。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">二世帯住宅には、以下の３つのタイプがあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">二世帯住宅とは、親と子供家族の二世帯が同じ敷地内の建物に同居することをいいます。 人生100年時代と呼ばれる超高齢社会を迎えたことや、現代の住宅取得等資金に関する贈与税の非課税措置などの税制優遇から、二世帯住宅へのリフォームが引き続き人気を集めています。<br />
<br />
親世帯にとっては「防犯」「家の維持管理」「もしもの時の安心」などの面でメリットがあり、子世帯にとっては「家事の軽減」「子供の面倒見」といったメリットがあります。 つまり二世帯住宅は、お互いが助け合う「相互扶助」の住まい方なのです。 特に、共働きが当たり前となった現代の子世帯にとっては、非常に魅力的な住まい方のひとつと言えます。 さらに近年では、生活費や光熱費の高騰対策として二世帯でコストをシェアする合理的な暮らし方としても再評価されています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">そんなニーズの高まりから、再び注目を集めているのが二世帯住宅です。二世帯リフォームの実績とノウハウが豊富なハウジングプラザが、現代のライフスタイルに合わせた二世帯リフォームのポイントをわかりやすく解説します。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">１つの建物を上下または左右に分け、それぞれ個別に住宅としての機能を持たせたもの。あるいは１つの建物ではなく２つの建物を庭や通路でつないだものが、「完全分離型二世帯住宅」です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">二世帯リフォームは、二世帯とも永く快適に暮らせるようプランニングしなければなりません。そのため、一般的なリフォームの場合とは比べ物にならないくらい、たくさんの問題の解決が求められます。たとえば、ドアをひとつ付けるか付けないかで、暮らしが大きく変わります。そのドアがあるために、親世帯・子世帯が心理的に気まずくなったり、逆に、いつまでも仲良くさせる秘密のドアになったり、二世帯リフォームは、ハード（住宅の構造やリフォームの仕様）よりも、ソフト（各世帯の気持ちや考え方）を大切に、プランニングを心がけたいものです。二世帯リフォームのご契約までの流れをご説明します。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">❶左右完全分離型<br />
１つの建物を「左右」に完全分離したタイプです。玄関を別々に設け、二世帯がそれぞれ個別に住宅としての機能を持つ間取りで暮らします。<br />
❷上下完全分離型<br />
１つの建物を「上下」に完全分離したタイプです。外階段を設け、上階にも専用の玄関を配置します。<br />
❸２棟完全分離型<br />
ひとつの敷地に二世帯がそれぞれ独立した住居を建築するタイプです。住居間には庭や通路などを設けて二世帯が行き来できるようにします。採光や通風が良くなるというメリットもあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">メリット</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">完全分離型二世帯リフォームのメリット・デメリット</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">二世帯リフォームのご契約までの流れ</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">部分共有型二世帯リフォームのメリット・デメリット</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">玄関共有型、玄関・浴室共有型、玄関・浴室・リビング共有型など、二世帯の住まい方の希望や状況に応じてさまざまなタイプが選べるのが、「部分共有型二世帯住宅」です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">❶玄関のみ共用<br />
玄関のみ共用し、他の間取りは二世帯それぞれに用意するタイプ。玄関ホールから世帯を左右に分離、または内階段で上下に分離します。帰宅、食事、入浴、就寝など、それぞれの世帯の生活時間でストレスなく過ごせます。<br />
❷玄関・浴室のみ共用<br />
玄関のみ共用型と比べて、浴室・脱衣場のスペースがゆったりとれます。<br />
❸玄関・浴室・リビングのみ共用<br />
キッチンを別々にすることで、親世帯・子世帯の食事やその時間のずれに対応できます。リビングも広くとることができます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">1. 二世帯でとことん話し合う<br />
まずは二世帯で時間をかけて、じっくり話し合う場をもちます。<br />
二世帯でどんな暮らしがしたいのか？<br />
そのためにどんな二世帯住宅にリフォームしたいのか？<br />
間取りについての親世帯の希望、子世帯の希望は？<br />
大枠の予算と負担の割合は？<br />
<br />
2. リフォームアドバイザーを交えて話し合う<br />
二世帯の間に第三者が入ると、世帯同士が互いに遠慮して、思っていることを口にできない場合があります。よって、アドバイザーはまず親世帯、次に子世帯と個別にヒアリングするのが一般的です。住宅設備や内装などの細かな仕様については、この段階で希望を伝えます。<br />
<br />
3. 提出されたリフォームプラン・お見積書を検討する<br />
アドバイザーが、ヒアリングの内容をもとにリフォームプランとお見積書を提示します。その説明を受け、じっくり検討しましょう。このとき、平面図だけでは暮らしのイメージがつかみにくいので、パースや鳥瞰図などを交えたプレゼンテーションツールを作成してもらうようにしましょう。<br />
<br />
4. 修正されたリフォームプラン・お見積書を検討する<br />
修正したリフォームプラン・お見積書を提示します。前回打ち合わせた内容が反映されているかよく確認し、分からないことや気になることは、担当アドバイザーに相談しましょう。暮らしやすい住まいにするため、二世帯でしっかりと話し合って、リフォームプランを決定しましょう。<br />
<br />
5. ご契約<br />
リフォームプランや見積金額など、全てに納得したらご契約です。リフォーム会社や担当アドバイザーとともに、理想の二世帯住宅をつくっていきましょう。<br />
二世帯リフォームの際には、登記や名義、税金、費用やローンなど、検討すべき点がたくさんありますので、専門家によく相談しましょう。ハウジングプラザは、二世帯リフォームに関するあらゆるご相談に対応します。知識と経験が豊富なリフォームアドバイザーにお気軽にご相談ください。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">二世帯リフォームジャンル別・ここがポイント！</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">「二世帯住宅を建てたいのだけれど、親の家が建っている土地では狭すぎる…」「かといって、郊外の土地に建てると、通勤が不便になる…」そんなジレンマをお持ちの方におすすめなのが、「中古住宅を購入して二世帯住宅にリフォーム」という選択肢です。既存物件の設備や内装などを活用しながら、二世帯住宅がローコストで実現し、親世帯にとっても子世帯にとっても便利な環境で暮らすことができるのが魅力です。ただし、中古住宅を購入して二世帯リフォームする際には、いくつかの注意点があります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">❶増築して二世帯住宅化する場合<br />
柱、梁、壁は増築に耐えられる強度があるか？<br />
２階を増築するなどの場合、その重さで１階に負担がかかり、強度不足になる場合があります。<br />
建築工法は？<br />
木造住宅の場合、主な工法に軸組工法（在来工法）と壁式工法（ツーバイフォー工法）があります。一般的に、増築に向いているのは軸組工法であるといわれています。<br />
❷二世帯住宅へ建て替える場合<br />
前面道路の幅員は？<br />
旧市街など、建物が密集する地区では、現在の建築基準法で定められた道路幅を満たしていない場合があり、建て替えが規制される場合があります。<br />
❸建ぺい率と容積率は？<br />
たとえば、100㎡の敷地で「建ぺい率50％・容積率100％」と定められている場合、家の建てられる建築面積は50㎡以下。総２階建てであれば、１階、２階合わせて100㎡以下にしなければなりません。増築の場合も建て替えの場合も注意が必要です。<br />
中古住宅を購入しての二世帯リフォームには、建築や不動産に関する広範で深い知識が求められます。頼れる専門家によく相談しながら進めましょう。ハウジングプラザは、リフォームの専門家であるリフォームアドバイザーと、不動産事業部の不動産アドバイザーが連携して、お客様に最適なプランを提案します。ぜひご相談ください。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">1. それぞれのライフスタイルが維持できる<br />
「完全同居型」「部分共有型」と比べて最も自由度の高いプランニングができるため、二世帯それぞれが、それぞれのライフスタイルを変えることなく過ごすことができます。<br />
2. お互いのプライバシーを尊重できる<br />
壁や床などを通じての音抜けなどがないため、お互いのプライバシーを尊重した住まい方が実現できます。<br />
3. ローン・税制面での優遇措置がある<br />
区分登記ができますので、それぞれの世帯が住宅ローンや税の優遇措置を受けることができます。<br />
デメリット<br />
1. 建築費が高くなる<br />
丸々２棟分とまではいかないまでも、場合によってはそれに近い建築コストがかかります。<br />
2. 土地が広くなければ不可能<br />
２棟間に庭や通路をとるには、それ相応の土地面積が必要です。無理して２棟タイプにすると、住みにくくなる恐れもあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">メリット<br />
1. それぞれのライフスタイルが維持できる<br />
「完全同居型」「部分共有型」と比べて最も自由度の高いプランニングができるため、二世帯それぞれが、それぞれのライフスタイルを変えることなく過ごすことができます。<br />
2. お互いのプライバシーを尊重できる<br />
壁や床などを通じての音抜けなどがないため、お互いのプライバシーを尊重した住まい方が実現できます。<br />
3. ローン・税制面での優遇措置がある<br />
区分登記ができますので、それぞれの世帯が住宅ローンや税の優遇措置を受けることができます。<br />
デメリット<br />
1. 建築費が高くなる<br />
丸々２棟分とまではいかないまでも、場合によってはそれに近い建築コストがかかります。<br />
2. 土地が広くなければ不可能<br />
２棟間に庭や通路をとるには、それ相応の土地面積が必要です。無理して２棟タイプにすると、住みにくくなる恐れもあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">メリット<br />
1. それぞれのライフスタイルが維持できる<br />
「完全同居型」「部分共有型」と比べて最も自由度の高いプランニングができるため、二世帯それぞれが、それぞれのライフスタイルを変えることなく過ごすことができます。<br />
2. お互いのプライバシーを尊重できる<br />
壁や床などを通じての音抜けなどがないため、お互いのプライバシーを尊重した住まい方が実現できます。<br />
3. ローン・税制面での優遇措置がある<br />
区分登記ができますので、それぞれの世帯が住宅ローンや税の優遇措置を受けることができます。<br />
デメリット<br />
1. 建築費が高くなる<br />
丸々２棟分とまではいかないまでも、場合によってはそれに近い建築コストがかかります。<br />
2. 土地が広くなければ不可能<br />
２棟間に庭や通路をとるには、それ相応の土地面積が必要です。無理して２棟タイプにすると、住みにくくなる恐れもあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">中古住宅を購入して二世帯リフォーム</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">メリット<br />
1. ある程度、お互いのプライバシーが尊重できる<br />
どこを共用し、どこを分けるかなどを、生活時間やリズムが異なる二世帯で話し合って決めることができます。<br />
2. 土地を有効活用できる<br />
玄関や設備を部分的に共有するため、限られた土地を有効に活用できます。<br />
デメリット<br />
1. 建築費が高くなる<br />
完全同居型に比べ、水まわりなどの工事や設備が増え、建築コストが高くなります。<br />
2. 二世帯間の考え・想いの調整が必要<br />
完全同居型リフォームよりもかなり自由な設計が可能となる分、「二世帯それぞれが専有するスペースの割合をどうするか」「二世帯間の生活時間のずれを認めながらどう解消するか」などについて、リフォーム担当者を交えた入念な打ち合わせが必要です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">マンションの二世帯リフォーム</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">マンションの設備・機器・間仕切り壁などを一度すべて撤去し、構造体（スケルトン）だけの状態にするところからリフォームを始めることを「スケルトンリフォーム」と呼びます。間取りはもちろん、水まわりなども移設・増設が可能ですから、スケルトンリフォームは、マンションの二世帯リフォームに最適なリフォーム方法です。スケルトンリフォームには、次のようなメリットがあります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">❶間取り全体を変更できる<br />
二世帯が共に快適に暮らせる自由な間取りを実現できます。<br />
❷水まわりの変更で「完全分離型」に近い二世帯化ができる<br />
「完全同居型」「部分共有型」二世帯マンションへのリフォームはもちろん、水まわりを増設すれば、「２ＬＤＫ＋１ＬＤＫ」（室内ドアで行き来できる）といった、「完全分離型」に近いリフォームも可能になります。<br />
ただし、多くの住人が暮らすマンションならではの注意点もあります。<br />
❶管理組合の管理規約を遵守する<br />
自宅のあるマンションでは、どこまでのリフォームが可能なのか、管理規約を確認しておきましょう。<br />
専有部分：構造体に触れなければ、大幅なリフォームが可能です<br />
共有部分：玄関を増設することはできません<br />
専用使用部分：避難通路でもあるバルコニーやベランダを壁で仕切ることはできません<br />
❷古いマンションは水まわりに注意<br />
古いタイプのマンションでは、水まわりの給排水管の構造が、比較的新しいマンションとは異なる場合があります。また、水道管を各戸に引き込む枝管が、構造体の中を貫通していることもあります。このような場合は、水まわりの位置を変えるリフォームは難しくなります。<br />
いずれにせよ、マンションを二世帯リフォームする際は、自宅のあるマンションでどこまでリフォームが可能なのかなど、事前に専門家のアドバイスを受けた上で判断することが大切です。ハウジングプラザには、マンションリフォームマネージャーなどの専門資格を有するアドバイザーが多数在籍しています。的確なアドバイスをさせていただきますので、安心してご相談ください。<br />
</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">❶玄関を２つに増やす<br />
親世帯・子世帯間の生活時間のずれによる暮らしにくさは、玄関を２つに増やして、完全分離型にすることなどで解決できます。こうすることによって、将来、どちらか一方の世帯を賃貸に出すことも可能です。<br />
❷水まわり（キッチン・浴室・トイレなど）を増やす<br />
キッチンを増やすことで、二世帯間の食事の違いや食事の時間差が解決できます。浴室・トイレを増やせば、二世帯の生活時間の束縛がかなり解消されます。<br />
❸部屋数を増やす<br />
寝室や子供部屋を増やすことは二世帯リフォームの基本。また、二世帯共用の納戸（物置部屋）をつくっておけば、居住スペースがすっきりします。<br />
一方、増築で二世帯住宅にリフォームするときには、「構造・工法上の問題」や「建築基準法の問題」をクリアにしておく必要があります。これらを熟知する専門家に相談して、プランを作成してもらいましょう。ハウジングプラザは、一級建築士などの資格を持つ専門家が、増築による二世帯リフォームをお考えの方にも親身に対応いたします。お気軽にご相談ください。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading03">二世帯リフォームの会社選び</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-right">
<a href="http://plaza-r.co.jp/archives/001/201308/large-52186bd31f91e.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52186bd31f91e.jpg"
 alt=""
 width="350"
 height="283">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">増築で二世帯住宅にリフォーム</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">二世帯住宅のバリアフリー＆断熱リフォーム</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">高齢の親世帯と、仕事や子育てに忙しい子世帯が同居するため、将来を見据えたバリアフリー設計は必須です。 さらに現代では「健康寿命」を伸ばすための住環境づくりが重視されています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">❶段差をなくす<br />
バリアフリーリフォームの基本です。家の中や外の段差を徹底的になくすことが、事故を未然に防ぎます。<br />
❷手すりをつける<br />
これもバリアフリーリフォームの基本。階段、廊下、浴室、玄関、玄関アプローチなど、要所要所に設置しましょう。<br />
❸ドアは引き戸にする<br />
外開きの扉は、向こう側に立つ人に当たる恐れがあります。引き戸にすると車椅子でもラクに通行できます。<br />
❹足元を明るくする<br />
歳をとると、小さな段差にもつまずきやすくなるもの。階段、廊下、玄関アプローチなどは足元を明るくしましょう。<br />
❺すべらない素材を使う<br />
廊下、階段、浴室、玄関アプローチなどに使用する素材には、転倒の心配が少ない、すべりにくいものを選びましょう。<br />
❻ヒートショック対策をする<br />
ヒートショック対策（断熱性向上）： 冬場に浴室や脱衣所で起こるヒートショックを防ぐため、浴室暖房機の設置や、窓の二重化など家全体の断熱性能を高めるリフォームが非常に重要です。\</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">将来を見越した二世帯リフォームを</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">❶完全同居型でも、間取りは変更できる余地を持たせておく<br />
子供は大きくなると自分の部屋を持ちたがります。また、暮らし方や趣味の変化で、間取りを変更したいという気持ちが湧いてくることも考えられます。これらを予め想定して、間仕切りは壁ではなく間仕切り家具にしておくなどの対策を考えておきましょう。<br />
❷完全分離型なら、将来は賃貸に出すこともできる<br />
１棟の建物を「上下」あるいは「左右」に分け、それぞれ独立した玄関を持つ二世帯住宅や、庭や通路でつないだ２棟タイプの二世帯住宅は、将来、どちらか一方の一世帯を賃貸に出して、副収入を得ることも可能です。<br />
❸バリアフリーリフォームをしておく<br />
親世帯の高齢化を見越してバリアフリー化しておくことも大切です。将来、親世帯が使っていた部屋を高齢になった子世帯が使い、子世帯の部屋を孫世帯が使うなどの「住み継ぎ」も考えておきましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">二世帯リフォームが完成したその時点では申し分のない住まいであっても、やがて子供が増え、成長し、また親世帯は介護が必要になってくるなど、家族構成やライフスタイルは、必ず大きく変わっていきます。ですから二世帯リフォームを行う際には、10年先・20年先から現在を見つめる視点が欠かせません。リフォームを考えるときは、どうしても「現在の満足」を追求してしまいがちですが、少し工夫しておくだけで、将来発生するリフォームのコストを抑えたり、副収入を得るチャンスを得たりすることができます。長期的な視点をもち、先手を打つリフォームを行いましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">増築という手段を使えば、二世帯リフォームのプランニングにグッと幅が出てきます。では、増築で何を増やすのか、そのパターンをいくつかご紹介しましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnitCol">





















<!-- 画像 -->
<div class="column-image-right">
<a href="http://plaza-r.co.jp/archives/001/201308/large-521883ee0cff6.png"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/521883ee0cff6.png"
 alt=""
 width="350"
 height="255">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>


















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>




















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group column-box-a">





















<!-- 画像 -->
<div class="column-image-right">
<a href="http://plaza-r.co.jp/archives/001/201308/large-52187770d0017.png"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52187770d0017.png"
 alt=""
 width="350"
 height="245">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-right">
<a href="http://plaza-r.co.jp/archives/001/201308/large-5218777866d16.png"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/5218777866d16.png"
 alt=""
 width="350"
 height="276">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-right">
<a href="http://plaza-r.co.jp/archives/001/201309/large-5233bd6fe2ff1.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201309/5233bd6fe2ff1.jpg"
 alt=""
 width="350"
 height="363">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>











<!-- テキスト -->

<p class="text01">二世帯リフォームを請け負う会社には、親世帯・子世帯で異なる心理や生活時間、生活パターンを調整し、まとめ上げていく、高度なヒアリング・プランニング能力が求められます。また、建築法規にも精通する必要があるため、お施主様の立場とすれば、安心して任せられるリフォーム会社に依頼したいものです。では、その会社選びはどのようにすれば良いのでしょうか。会社の規模や技術力のほかに、押さえておきたいポイントをまとめてみました。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">ポイント1<br />
その会社には、二世帯住宅で暮らす人の目線、気持ちになれるアドバイザーや建築士がいるか？<br />
ポイント2<br />
その会社は、二世帯双方の話をよく聞き、理解し、納得できる提案をしてくれるか？<br />
ポイント3<br />
その会社は、二世帯リフォームの豊富な経験と実績があるか？<br />
ハウジングプラザは、知識と経験豊富な専門家が、お客様の立場に立って、最適な提案と施工を行います。プランニングとお見積は無料ですので、ぜひ一度、お気軽にご相談ください。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-left">
<a href="http://plaza-r.co.jp/archives/001/201308/large-52186bd336db6.jpg"data-rel="SmartPhoto" data-caption="上下分離型">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52186bd336db6.jpg"
 alt=""
 width="350"
 height="318">
</a>
<p class="caption">上下分離型</p>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-left">
<a href="http://plaza-r.co.jp/archives/001/201308/large-52186bd33938e.jpg"data-rel="SmartPhoto" data-caption="左右分離型">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52186bd33938e.jpg"
 alt=""
 width="350"
 height="318">
</a>
<p class="caption">左右分離型</p>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<a href="http://plaza-r.co.jp/archives/001/201308/large-52186bd33c6e3.jpg"data-rel="SmartPhoto" data-caption="2棟完全分離型">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52186bd33c6e3.jpg"
 alt=""
 width="350"
 height="318">
</a>
<p class="caption">2棟完全分離型</p>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>目的・状況別リフォーム</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/reform-situation/pickup-twogenehouse.html</guid>
<pubDate>Sat, 24 Aug 2013 17:16:19 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>住宅診断・耐震診断</title>
<link>http://plaza-r.co.jp/guide/reform-situation/diagnosis.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">安心のリフォーム・中古住宅購入・売却のために…</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">建物に精通した建築士が、専門家の見地からご自宅や購入予定の中古住宅などについて総合的な住宅診断・耐震診断を行います。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03"><span class="color-blue">住宅診断・耐震診断の内容</span></h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">診断は、建築士などの有資格者が、診断器具を使用しながら目視で、基礎や外壁、普段目の届かない床下や小屋裏まで行い、診断結果は報告書を作成して、提出させていただきます。診断に要する時間は、概ね2〜3時間程度です。お住まいの劣化や不具合がないかを診断する「住宅診断」、地震が発生した時に倒壊の恐れがないかを診断する「耐震診断」の内容は次のとおりです。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->



























<!-- テーブル -->
<div class="column-table">
  <div >
  <table class="table01 is_scrollHint">
<tr>
<th class="acms-cell-text-nowrap acms-admin-cell-text-nowrap">診断項目</th>
<th class="acms-cell-text-nowrap acms-admin-cell-text-nowrap">内容</th>
<th>対象部位</th>
</tr>
<tr>
<td>住宅診断</td>
<td>現況診断（目視）</td>
<td>各部の使用材料やひび割れ・変色・腐朽などの状況を、診断器具を使用しながら目視で診断します。</td>
</tr>
<tr>
<td>個別性能診断</td>
<td>性能評価</td>
<td>バリアフリー性、防耐火性、設備の維持管理容易性、健康性、自然エネルギー利用の可能性などを確認します。</td>
</tr>
</table>

  </div>
</div>
































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">住宅診断・耐震診断の費用</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->



























<!-- テーブル -->
<div class="column-table">
  <div >
  <table class="table01 is_scrollHint">
<tr>
<th class="acms-cell-text-nowrap acms-admin-cell-text-nowrap">診断内容</th>
<th class="acms-cell-text-nowrap acms-admin-cell-text-nowrap">対象</th>
<th class="acms-cell-text-nowrap acms-admin-cell-text-nowrap">料金（税込）</th>
</tr>
<tr>
<td> 住宅診断</td>
<td> 一戸建</td>
<td>66,000円／件</td>
</tr>
<tr>
<td> 住宅診断</td>
<td> マンション</td>
<td>55,000円／件</td>
</tr>
<tr>
<td> 耐震診断</td>
<td> 一戸建</td>
<td>44,000円／件</td>
</tr>
</table>

  </div>
</div>
































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">耐震診断につきましては、木造軸組工法の一戸建住宅のみとさせていただきます。<br />
リフォーム工事、中古住宅の購入・中古住宅の売却を弊社にご相談いただいている方については、上記の費用を全て無料とさせていただきます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">ハウジングプラザの住宅診断・耐震診断の特徴<br />
</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>診断はすべて建築士が対応！</li>
<li>リフォーム等のご相談をいただいている方は無料で診断！</li>
<li>詳しくわかりやすい診断報告書を提出！</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">住宅診断・耐震診断の流れ！</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">住宅診断・耐震診断は下記の5つの流れとなっております。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">1. お申し込み<br />
お申し込みは、お電話（フリーダイヤル0120-294-849）または「診断お申し込み書」のFAX（フリーダイヤル0120-001-849）にて承ります。※弊社にリフォーム工事等をご相談中の方は担当者にお申し付けください。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">2. 必要書類のご準備とヒアリング<br />
購入時や新築時の販売資料やパンフレット、設計図書など、できるだけ多くの資料をご準備願います。資料を事前に郵送していただくケースもございますが、診断担当者がお電話や面談にてヒアリングをさせていただきます。※資料がない場合でも、診断は可能です。<br />
<br />
3. 現地調査<br />
弊社規定の調査項目に基づき、屋外、室内、床下、小屋裏、設備などの総合的な調査を行います。※マンションの耐震診断は承っておりません。住宅診断のみとなりますのでご了承ください。<br />
<br />
4. 診断および診断報告書の作成<br />
設計図書などの資料や現地調査結果などに基づき診断を行い、診断報告書を作成させていただきます。<br />
<br />
診断結果を詳しく、わかりやすくまとめた診断報告書。必要に応じて、診断者からのアドバイスや参考補修方法も表示します。<br />
5. 診断報告書のご説明<br />
<br />
診断報告書をお持ちし、詳しくご説明します。ご不明な点は何なりとご質問ください。<br />
ご希望のお客様には、無料でリフォームプランの作成とお見積りもさせていただきます！！</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">こんな方は、ぜひご利用ください！</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">1.リフォームをお考えの方！</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">以前から建物に気になる所があるが、一度も専門家にみてもらったことがないので心配…</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 
<h5 class="heading05">診断によるメリット</h5>

























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">問題の早期発見と対処ができ、結果的にコストが割安になります！</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">問題の早期発見と対処ができ、結果的にコストが割安になります！<br />
全面改装や水まわり改修などの大がかりなリフォームを考えている…<br />
診断によるメリット<br />
構造面や耐震性能を診断することで、安全性や質の高いプランニングが可能になります！<br />
リノベーションか建て替えかで悩んでいる…<br />
◎診断によるメリット<br />
リノベーションか建て替えの判断材料を提供します！<br />
耐震性能を確認しないまま住んでいるので不安…<br />
◎診断によるメリット<br />
耐震診断を行うことで、補強工事の必要性の有無が判断できます！</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->

<hr class="hr01" />


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-left">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/521319f699266.png"
 alt=""
 width="250"
 height="264">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->













<div class="column-group groupUnit02">
















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->


























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











</div>























<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52131aa47f5bf.png"
 alt=""
 width="250"
 height="259">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52131ac0cb318.png"
 alt=""
 width="250"
 height="220">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-left">
<a href="http://plaza-r.co.jp/archives/001/201308/large-520c77f67a3cd.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/520c77f67a3cd.jpg"
 alt=""
 width="665"
 height="442">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-left">
<a href="http://plaza-r.co.jp/archives/001/201308/large-520c77ff9a74c.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/520c77ff9a74c.jpg"
 alt=""
 width="665"
 height="442">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-left">
<a href="http://plaza-r.co.jp/archives/001/201308/large-520c780bdfebc.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/520c780bdfebc.jpg"
 alt=""
 width="665"
 height="442">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/520c781729a78.jpg"
 alt=""
 width="450"
 height="300">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<a href="http://plaza-r.co.jp/archives/001/201308/large-520c7996df794.png"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/520c7996df794.png"
 alt=""
 width="1200"
 height="296">
</a>
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-right">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52131aef79160.png"
 alt=""
 width="250"
 height="164">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52131b17d7651.png"
 alt=""
 width="250"
 height="182">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52131b3b1c931.png"
 alt=""
 width="250"
 height="221">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/001/201308/52131b4d21686.png"
 alt=""
 width="250"
 height="193">
</div>






























<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>目的・状況別リフォーム</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/reform-situation/diagnosis.html</guid>
<pubDate>Wed, 03 Jul 2013 18:23:38 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>太陽光発電・蓄電池・HEMS</title>
<link>http://plaza-r.co.jp/guide/choose-equipment/itemguide-sunlight.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">太陽光発電ではじめる、新しいエネルギーライフ</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">太陽光発電システムは、単に「電気を創る」時代から、蓄電池や電気自動車（EV）と連携してエネルギーを「賢く使い切る」時代へと大きく進化しています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">「創る」「蓄える」「賢く使う」で、もっと豊かで安心な暮らしへ</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">１．屋根の美しさを損なわない、洗練されたデザイン</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">これからの太陽光発電は、発電効率だけでなく住宅の外観との調和も重要です。「MODULUS（モデュラス）ブラックモデル」のようなデザイン性に優れた太陽電池モジュールを選ぶことで、住まいの美しさを保ちながら最新のエコライフを実現できます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">２．地球とおサイフに優しい「Wエコ」の力</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">太陽光発電の最大の魅力は「エコロジー＆エコノミー」です。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>エコロジー：発電時のCO₂排出量はゼロ。枯渇する心配のないクリーンな太陽エネルギーを利用し、地球環境と子どもたちの未来に貢献します。</li>
<li>エコノミー：発電した電気を家庭で使い、余った電気は電力会社へ売電（電気を売る）。雨天や夜間など発電量が足りない時は買電（電気を買う）することで、無駄なく電気代を節約できま</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">３．クルマと家をつなぐ最新技術「V2H」</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">電気自動車（EV・PHEV）をご検討中の方に欠かせないのが「V2H蓄電システム（eneplatなど）」です。EVを単なる移動手段としてだけでなく「家庭用の巨大なバッテリー」として活用することで、昼間発電した電気をEVに蓄え、夜間に家庭で使うといった、さらに進んだエネルギーの自給自足を目指せます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">４．災害に強い家をつくる「創蓄連携システム」</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">従来の太陽光発電だけでも、停電時に自立運転コンセントから最大1500Wの電力を使用し、最低限の電化製品を動かすことが可能でした。さらに最新の「創蓄連携システム」や「リチウムイオン蓄電システム（可搬型バッテリー e-blockなど）」を組み合わせることで、万が一の災害時にもより長時間、安定して電気を使える強靭（レジリエンス）な住まいが実現します。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">５．家中をスマートに管理する「HEMS（AiSEG3）」</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">「AiSEG3」をはじめとするホームエネルギーマネジメントシステム（HEMS）や電力計測制御ユニットを導入すれば、家全体のエネルギー使用状況を見える化し、最適にコントロールすることが可能です。創った電気を一番賢く使う、次世代のスマートハウスの司令塔となります。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>設備・商品の選び方</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/choose-equipment/itemguide-sunlight.html</guid>
<pubDate>Thu, 27 Jun 2013 09:28:35 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>オール電化</title>
<link>http://plaza-r.co.jp/guide/choose-equipment/itemguide-allelectricsystem.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">オール電化・太陽光・蓄電池・HEMSで叶える、未来のスマートな暮らし！</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">ガスコンロを「IHクッキングヒーター」に、給湯器を「エコキュート」に取り替え、家庭内のエネルギーをすべて電気でまかなう「オール電化」。現在、九州電力管内の新築戸建て住宅におけるオール電化採用率はなんと75%（2020〜2024年度平均）に達しています。<br />
現在のトレンドは、オール電化に**「太陽光発電」「蓄電池」「HEMS」**を組み合わせた最先端のスマートハウスです。光熱費を劇的に削減しながら、災害にも強い安心の暮らしを実現する最新スタイルをご紹介します。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">オール電化＋αで実現する4つの大きなメリット</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">１．「圧倒的な経済性」〜電気をつくり、貯めて、賢く使う〜</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">基本料金を電気のみに一本化し、夜間の割安な電気でお湯を沸かすことで、毎月の固定費と光熱費を抑えることができます。 さらに太陽光発電を組み合わせれば、昼間の電気代を自家発電でまかない、使いきれずに余った電気を電力会社に売る（売電）ことが可能です。ご家庭の条件によっては、光熱費を年間約80％も削減することが期待できます。 また、蓄電池を導入することで、太陽光でつくった電気を貯めて夜間に使ったり、効率よくエネルギーを運用することが可能になります（※「再エネ蓄電サービス」などのプランも用意されています）。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">２．HEMS（AiSEG3など）による「見える化」と「自動制御」</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">最新の住まいに欠かせないのが、家中のエネルギーを管理するHEMS（ホームエネルギーマネジメントシステム）です。パナソニックの「AiSEG3」などのシステムを導入すれば、家中の電化製品を連携させることが可能です。 リアルタイムで電気の使用状況がわかる「カラー電力モニタ」などを活用することで、節電の励みになり、家族みんなの省エネ意識も高まります。天気予報と連動してエコキュートの沸き上げを自動でコントロールするなど、無駄のない効率的なエネルギー管理を実現します。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">３．万が一の時も安心！家族を守る「防災力」と「安全性」</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">火を一切使わないため、火災や不完全燃焼の心配がなく、小さなお子さまやご年配の方でも安心です。 災害時にもこのシステムは大きな強みを発揮します。太陽光発電と蓄電池があれば、停電時でも日中に電気をつくり、貯めた電気を夜間に使うことができるため安心です。また、エコキュートの貯湯タンクには常にお湯（または水）が満タンに入っており、万が一の断水時には非常用水として生活用水に活用できます。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">４．クリーンな「環境性」と驚きの省エネ</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">家庭の消費エネルギーの約30%を占めるのが「給湯」ですが、空気の熱を利用するエコキュートなら、電気だけで沸かす場合に比べて電力消費量を約1/3に抑えられます。さらに太陽光発電は発電時にCO2を出さないため、環境に極めてやさしいクリーンなエネルギーシステムです。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03"> 毎日をもっと快適に！最新設備のココがすごい</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04"> IHクッキングヒーター</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>高火力＆スピーディ：熱効率が約90%と高く、ムダなく鍋に熱を伝えるため調理がスピーディです。</li>
<li>お手入れラクラク：平らなガラストップで油煙が飛び散りにくく、サッと拭くだけでお掃除完了。壁や換気扇のベタベタ汚れも大幅に軽減されます。夏場でもキッチンが暑くなりにくいのも魅力です。</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading04">エコキュート</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>ワンランク上のバスタイム：「美泡湯eco」や「ウルトラファインバブル」などの最新機能で、毎日のお風呂が極上のリラックス空間に変わります。</li>
<li>未来の暮らしに対応：太陽光発電の電気を有効活用してお湯を沸かす「おひさまエコキュート」など、スマートハウスに対応した最先端のモデルが豊富です。</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">「初期費用が高い？」「工事が大変？」という方へ</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">リフォームでの導入は、想像以上に手軽でサポートも充実しています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>工事はスピーディ：エコキュートの標準的な設置工事はわずか1〜2日で完了。キッチンのガスコンロ部分のみをIHに取り替えるプチリフォームも可能です。</li>
<li>おトクな補助金制度：現在、国による「給湯省エネ2025事業」など、エコキュート導入に対する手厚い補助金制度が実施されています。</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">エネルギーを自給自足し、毎日の暮らしを安心・快適・おトクにしてくれる「オール電化＋太陽光発電＋蓄電池＋HEMS」。ぜひこの機会に、最新のスマートハウス化をご検討してみてはいかがでしょうか！</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>設備・商品の選び方</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/choose-equipment/itemguide-allelectricsystem.html</guid>
<pubDate>Thu, 27 Jun 2013 09:06:52 +0900</pubDate>
</item>
<item>
<dc:creator>HP管理者</dc:creator>
<title>エクステリア</title>
<link>http://plaza-r.co.jp/guide/choose-equipment/itemguide-exterior.html</link>
<description><![CDATA[















































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">住まいの第一印象を決めるエクステリアは、美しさだけでなく、現代のライフスタイルに合わせた機能性が求められています。カーポート、ウッドデッキ、門扉、フェンスの最新機能と選び方のポイントをご紹介します。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->


<h2 class="heading02">住まいを彩る、エクステリアの選び方</h2>



 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">１．門扉・アプローチ：スマート化と宅配ボックスの普及</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">毎日のように出入りする門まわりは、利便性と防犯性を両立させるのが最新のトレンドです。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




<h4 class="heading03">２．車庫まわり・カーポート：強靭さと環境への配慮</h4>

 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>住宅に馴染むフラットルーフ 従来の丸みを帯びた屋根だけでなく、シンプルでノイズレスな直線的デザインの「プレーンルーフ」や「エフルージュ」シリーズがトレンドです。住宅の景観を美しく引き立てます。</li>
<li>災害に強い高強度カーポート 台風や大雪といった激しい気象条件にも耐えうる、強度性能に優れた折板屋根カーポート「ジーポートPro」シリーズが注目されています。</li>
<li>ソーラーパネル搭載（創エネ） 環境への配慮から、屋根部分に太陽光パネルを搭載できる「ジーポートPro PV」のような次世代型カーポートも登場しており、駐車スペースを有効活用してエネルギーを生み出すことが可能です。</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">４．フェンス：美しく視線を遮るスクリーンフェンス</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">単なる雨よけから、住宅デザインとの調和や自然災害への備え、さらにはエネルギー創出の場へと進化しています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">３．庭まわり（テラス・ウッドデッキ）：リビングの延長としての快適空間</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>デザイン性とプライバシーの両立 外からの視線を遮る目隠しフェンスは、よりスタイリッシュに進化しています。特に「ルシアス スクリーンフェンス」のような、高さを出しつつもスリット（縦格子・横ルーバーなど）によって風通しと光を確保できるデザインが人気です。</li>
<li>高所用・防音などの高機能化 安全性を高める歩行補助手すりや転落防止柵、高所用フェンスのほか、パブリック向け技術を応用した防音目隠しフェンスなど、設置環境の悩みを解決する機能性フェンスも充実しています。</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->



<h3 class="heading03">選び方のポイント</h3>


 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">最新のエクステリアプランニングでは、デザインの統一感（コーディネート）だけでなく、「将来の車のサイズアップ」「宅配便の利用頻度」「庭での過ごし方」など、ご家族の現在のライフスタイルと数年後の変化を見据えて機能を選ぶことが重要です。最新のカタログを参考に、デザインと機能性を両立させた理想の空間づくりを目指しましょう。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">お庭は「見る場所」から、家族で「過ごす場所」へと変化しています。</p>




 
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>メンテナンスフリーな「再生木」デッキ ウッドデッキは、天然木のような温かみと質感を持ちながら、腐食しにくくお手入れが簡単な「リウッド（再生木）」を使用した製品（リウッドデッキ Sなど）が現在の主流です。</li>
<li>テラス屋根・テラス囲いで全天候型に 急な雨から洗濯物を守るだけでなく、「ソラリア」シリーズなどのテラス屋根やテラス囲い（ガーデンルーム）を設置することで、花粉やPM2.5を気にせず過ごせる快適なマルチスペースが完成します。</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->




 

<ul class="ul01">
<li>電気錠（スマートコントロールキー）の導入 従来の鍵に加え、現在は鍵を取り出さずに施解錠できる「ポケットキーシステム」などの電気錠付門扉が人気を集めています。荷物で両手が塞がっている時でもスムーズに出入りでき、防犯性も高まります。</li>
<li>宅配ボックスの標準化 ネットショッピングの普及に伴い、インターホンや表札をまとめた機能ポール（門柱）に「宅配ボックス」や「宅配ポスト」を組み込むスタイルが定番化しています。スマートなデザインの「ルシアス 宅配ボックス」など、外観を損なわずに荷物を受け取れるアイテムが豊富です。</li>
<li>デザインテイストの多様化 重厚感のある高級シリーズ「エクスティアラ」、木調パネルでモダンな「ルシアス」、クラシックなアルミ鋳物の「シャローネ」、シンプルで合わせやすい「シンプレオ」など、住まいの外観に合わせてトータルコーディネートが可能です。</li>
</ul>
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>設備・商品の選び方</category>
<guid isPermaLink="true">http://plaza-r.co.jp/guide/choose-equipment/itemguide-exterior.html</guid>
<pubDate>Thu, 27 Jun 2013 08:58:14 +0900</pubDate>
</item>
</channel>
</rss>
