<?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/saga/blog/</link>
<atom:link href="http://plaza-r.co.jp/saga/blog/rss2.xml" rel="self" type="application/rss+xml" />
<language>ja</language>
<copyright>Copyright (C) 2026 ブログ All rights reserved.</copyright>
<lastBuildDate>Sun, 05 Apr 2026 14:16:07 +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/saga/blog/entry-13442.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">こんにちは！ハウジングプラザの杉屋です！<br />
<br />
4月になり、社会人2年目に入りました。<br />
気持ちを新たに引き締めて、これからもたくさんのお客様にご満足いただけるよう頑張っていきます！<br />
それでは今回はトイレ改装工事のご紹介をさせていただきます！<br />
<br />
今回は、大便器の暖房便座やお掃除機能の不具合に加え、小便器も長年の使用による汚れが目立っており、さらに手洗い器ではハンドル部分からの水漏れが発生しているとのことでご相談をいただきました。<br />
<br />
今回のご相談に対して、当社からは、大便器についてはお掃除しやすいフチなし形状のTOTO「ピュアレストEX」と、便器内まで除菌水で洗浄できるアプリコットシリーズをご提案しております。<br />
また、小便器も同様に清掃性に優れたフチなし形状で、ボウル面の傾斜により尿はねを軽減するタイプをご提案し、手洗い器については水栓金具の取替をご提案しております。<br />
あわせて、便器の取替に伴い床のCFシートも貼替を行い、トイレ空間全体を一新するご提案をさせていただいております。<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-center acms-col-sm-6">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202604/bbfb832a8772d86cfc420fb63064d4b3.jpg"
 alt=""
 width=""
 height="">
</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 acms-col-sm-6">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202604/0b1c448668d78ff8129742e1c7eb6ae3.jpg"
 alt=""
 width=""
 height="">
</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 acms-col-sm-6">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202604/04819effbaa6c99257d4a08fbf81974b.jpg"
 alt=""
 width=""
 height="">
</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">工事後のトイレ空間がこちらになります。</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 acms-col-sm-6">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202604/912367311bb30d9b38166b727c6b58b3.jpg"
 alt=""
 width=""
 height="">
</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 acms-col-sm-6">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202604/1bfec5bd53c3e94a35c65e13bf2d2b75.jpg"
 alt=""
 width=""
 height="">
</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 acms-col-sm-6">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202604/3d2b8d2cabff5bde8bb43e57e164b4ae.jpg"
 alt=""
 width=""
 height="">
</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 acms-col-sm-6">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202604/70e1722a9c5c327f1f6b6ba9b75a6e6f.jpg"
 alt=""
 width=""
 height="">
</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 />
また、床のCFシートも貼替を行ったことでトイレ空間が一新され、お客様にも大変ご満足いただけました！<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）。ユニット側では
     どちらも持たない。
-->


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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/saga/blog/entry-13442.html</guid>
<pubDate>Sun, 05 Apr 2026 14:16:07 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>玄関ドア取替の紹介</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13436.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">こんにちは！佐賀店工務の久です。<br />
春も近づき温かくなってきましたね。<br />
4月になれば佐賀に来て1年になりますがとても早い1年でした。<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202603/large-b84a83512a3e56996aec9690c2e29d71.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202603/b84a83512a3e56996aec9690c2e29d71.jpg"
 alt=""
 width="340"
 height="450">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">こちらを既存の玄関を壊さず1日で取替えました。<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-center acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202603/large-60a681830894c4622397c40f5e1e9626.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202603/60a681830894c4622397c40f5e1e9626.jpg"
 alt=""
 width="340"
 height="482">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">ガラッと雰囲気が変わり、木目が素敵な玄関に仕上がりました。<br />
工事も1日で終わりますので、防犯面も安心できます。<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<guid isPermaLink="true">http://plaza-r.co.jp/saga/blog/entry-13436.html</guid>
<pubDate>Mon, 30 Mar 2026 19:22:50 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>空間を利用した間取り変更工事(後編）</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13429.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">お久しぶりです。<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）。ユニット側では
     どちらも持たない。
-->


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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/saga/blog/entry-13429.html</guid>
<pubDate>Thu, 26 Mar 2026 21:31:06 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>玄関ドア取替工事のご紹介</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13418.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">皆様こんにちは！<br />
佐賀店アドバイザーの高森です。<br />
<br />
日中ぽかぽかで暖かい日が多くなりましたね。<br />
桜の開花予想も出ていて、お花見が待ち遠しいです～^^<br />
<br />
さて、今回は玄関ドア取替工事のご紹介です。<br />
35年ほどお使いで、鍵の不具合をきっかけにご相談いただきました。<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<guid isPermaLink="true">http://plaza-r.co.jp/saga/blog/entry-13418.html</guid>
<pubDate>Fri, 20 Mar 2026 09:24:18 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>花粉症シーズン到来</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13413.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">花粉症のシーズン真っ只中です。<br />
19歳から花粉症デビューした古川です。<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）。ユニット側では
     どちらも持たない。
-->


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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/saga/blog/entry-13413.html</guid>
<pubDate>Sat, 14 Mar 2026 08:26:06 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>相談会開催中！！</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13404.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">こんにちは、事務の赤司です。<br />
現在、TOTOショールームにて相談会を開催中です！<br />
本日までの開催となっておりますので、<br />
ぜひお気軽にお立ち寄りください。<br />
<br />
暖かくなってきたとはいえ、<br />
まだ朝晩は冷える日もありますよね。<br />
<br />
お風呂に入ったときのヒヤッとする床や、<br />
すぐに冷えてしまう浴槽に<br />
「寒い…」と感じたことはないでしょうか。<br />
<br />
最近のユニットバスは、<br />
暖かく快適に使える工夫がたくさんあります。<br />
<br />
今回はTOTOのユニットバスを少しご紹介します！<br />
<br />
まず、浴室に入ったときの床のヒヤッと感を<br />
やわらげてくれるのが「ほっカラリ床」。<br />
<br />
床の内側に断熱材が入っているため<br />
冷気を遮断し、<br />
足元が冷たくなりにくいのが特徴です。<br />
<br />
さらに 「高断熱浴槽」 は浴槽を<br />
断熱材で包み込んでいるため、<br />
お湯が冷めにくく時間が経っても<br />
温かいお風呂を楽しめます。<br />
<br />
家族が入るタイミングを<br />
気にしなくていいのも嬉しいポイントです。<br />
<br />
また、浴室暖房乾燥機の設置や<br />
窓の断熱リフォームなどで、<br />
さらに快適な浴室にすることも可能です。<br />
<br />
実際の商品はTOTOショールームでご覧いただけます。<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）。ユニット側では
     どちらも持たない。
-->


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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/saga/blog/entry-13404.html</guid>
<pubDate>Sun, 08 Mar 2026 08:38:00 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>キッチン丁番・間仕切り建具取替工事！</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13396.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">こんにちは！ハウジングプラザの杉屋です！<br />
<br />
3月になりましたね。<br />
まもなく社会人2年目を迎えます。<br />
<br />
1年目で身につけた社会人としてのマナーや基礎をしっかりと活かしながら、2年目はさらに成長できるよう、一層努力してまいります！<br />
それでは今回はキッチン丁番・間仕切り建具取替工事のご紹介をさせていただきます！<br />
<br />
お客様より「丁番が外れてしまったので丁番を取替て欲しい」とのご相談をいただきました。<br />
現地で確認したところ、1か所の丁番が完全に外れており、さらにその丁番が取り付けられている間仕切り建具自体も、経年劣化により下地部分が傷んでいる状態でした。丁番だけを交換しても十分に固定できない可能性があったため、今回は丁番の取替に加え、間仕切り建具本体の取替もあわせてご提案・施工させていただきました。<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-center acms-col-sm-12">
<a href="http://plaza-r.co.jp/archives/055/202603/large-b1fce66783c82f37b0095ec7f5fb07a4.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202603/b1fce66783c82f37b0095ec7f5fb07a4.jpg"
 alt=""
 width="700"
 height="525">
</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 acms-col-sm-12">
<a href="http://plaza-r.co.jp/archives/055/202603/large-a35900fac5ecc7c965636f15e64a6850.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202603/a35900fac5ecc7c965636f15e64a6850.jpg"
 alt=""
 width="700"
 height="525">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<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 acms-col-sm-12">
<a href="http://plaza-r.co.jp/archives/055/202603/large-69219a84af11a42ebf4cf1b1d8eb76a1.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202603/69219a84af11a42ebf4cf1b1d8eb76a1.jpg"
 alt=""
 width="700"
 height="934">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">建具屋さんに既存と同じ形状で間仕切り建具を製作していただき、あわせて劣化していた丁番も取替させていただきました。<br />
キッチンは毎日使う場所のため、長年の開閉によって丁番が緩んだり傷んだりし、扉がきちんと閉まらなかったり傾いたりすることがあります。<br />
日常的に使う場所だからこそ、小さな不具合でも大きなストレスにつながります。<br />
今回の工事によってそのお悩みを解消することができ、お客様にも大変ご満足していただくことができました！<br />
<br />
今回の工事にご興味を持たれた方、あるいは他のリフォームをお考えの方には、お見積りや現場調査を無料で承っております。<br />
ぜひお気軽にご相談ください！<br />
<br />
そして今週末はTOTOショールームにて「新築＋リフォーム大相談会」を開催します！<br />
○日時：3月7日（土）・8日（日）の2日間<br />
○場所：TOTO佐賀ショールーム<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）。ユニット側では
     どちらも持たない。
-->


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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/saga/blog/entry-13396.html</guid>
<pubDate>Mon, 02 Mar 2026 11:07:03 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>空間を利用した間取り変更工事(前編)</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13392.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">お久しぶりです。<br />
アドバイザーの原口です。<br />
<br />
気づけば2月も終わり、私も入社して2年が過ぎようとしてます。<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 />
<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-center acms-col-sm-12">
<a href="http://plaza-r.co.jp/archives/055/202602/large-ca374b128850d05aa048a91cac47c0b7.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/ca374b128850d05aa048a91cac47c0b7.jpg"
 alt=""
 width="700"
 height="934">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">今回は浴室を1616サイズにして、余ったスペースに収納を作りました。<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center acms-col-sm-12">
<a href="http://plaza-r.co.jp/archives/055/202602/large-61764cbc0c96c165fec121e5f35b2aab.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/61764cbc0c96c165fec121e5f35b2aab.jpg"
 alt=""
 width="700"
 height="934">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<guid isPermaLink="true">http://plaza-r.co.jp/saga/blog/entry-13392.html</guid>
<pubDate>Fri, 27 Feb 2026 18:07:51 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>浴室・洗面工事の紹介</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13384.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">こんにちは！佐賀店工務の久です。<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202602/large-97f0abb0f30ed7848018b513f2a0281a.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/97f0abb0f30ed7848018b513f2a0281a.jpg"
 alt=""
 width="340"
 height="458">
</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 acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202602/large-42eab5308b9e19d4820b8882fd0413ac.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/42eab5308b9e19d4820b8882fd0413ac.jpg"
 alt=""
 width="340"
 height="488">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202602/large-31a3875167a0e3496438c1a177454a02.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/31a3875167a0e3496438c1a177454a02.jpg"
 alt=""
 width="340"
 height="454">
</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 acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202602/large-d0b1c5c252148a479f92c62d1462baf9.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/d0b1c5c252148a479f92c62d1462baf9.jpg"
 alt=""
 width="340"
 height="463">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->





























<!-- 画像 -->
<div class="column-image-center acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202602/large-d32d2e37861a71aa4ba8cd6f102966a4.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/d32d2e37861a71aa4ba8cd6f102966a4.jpg"
 alt=""
 width="340"
 height="453">
</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 acms-col-sm-6">
<a href="http://plaza-r.co.jp/archives/055/202602/large-2bc0d50ee8ad535849b39afd15458ded.jpg"data-rel="SmartPhoto" data-caption="">
<img class="columnImage"
 src="http://plaza-r.co.jp/archives/055/202602/2bc0d50ee8ad535849b39afd15458ded.jpg"
 alt=""
 width="340"
 height="460">
</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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->

















<!-- テキスト -->

<p class="text01">浴室もユニットバスでお手入れや断熱効果も高まり快適になりました。<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）。ユニット側では
     どちらも持たない。
-->


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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/saga/blog/entry-13384.html</guid>
<pubDate>Fri, 20 Feb 2026 15:07:05 +0900</pubDate>
</item>
<item>
<dc:creator>更新作業担当</dc:creator>
<title>浴室改装工事のご紹介</title>
<link>http://plaza-r.co.jp/saga/blog/entry-13376.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">皆様こんにちは！<br />
佐賀店アドバイザーの高森です。<br />
<br />
まだまだ寒い日が続きますね。<br />
体調に気を付けて頑張ります！<br />
<br />
今回は浴室改装工事のご紹介です。<br />
35年ほど使われており、浴室の寒さと掃除のしにくさが気になっておられました。<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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<guid isPermaLink="true">http://plaza-r.co.jp/saga/blog/entry-13376.html</guid>
<pubDate>Sat, 14 Feb 2026 09:30:30 +0900</pubDate>
</item>
</channel>
</rss>
